package com.vastcom.sbjwt.security;

import com.vastcom.sbjwt.common.TimeProvider;
import com.vastcom.sbjwt.model.User;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mobile.device.Device;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

@Component
public class TokenHelper {
    @Value("${app.name}")
    private String APP_NAME;

    @Value("${jwt.secret}")
    private String SECRET;

    @Value("${jwt.expires_in}")
    private int EXPIRES_IN;

    @Value("${jwt.mobile_expires_in}")
    private int MOBILE_EXPIRES_IN;

    @Value("${jwt.header}")
    private String AUTH_HEADER;


    static final String AUDIENCE_UNKNON="unknown";
    static final String AUDIENCE_WEB="web";
    static final String AUDIENCE_MOBILE="mobile";
    static final String AUDIENCE_TABLET="tablet";

    @Autowired
    TimeProvider timeProvider;

    private SignatureAlgorithm SINGNATURE_ALGORITHM=SignatureAlgorithm.HS512;

    public String getUsernameFromToken(String token){
        String username;
        try {
            final Claims claims=this.getAllClaimsFromToken(token);
            username=claims.getSubject();
        } catch (Exception e) {
           username=null;
        }
        return username;

    }

    public Date getIssuredAtDateFromToken(String token){
        Date issueAt;
        try {
            final Claims claims=this.getAllClaimsFromToken(token);
            issueAt=claims.getIssuedAt();
        } catch (Exception e) {
           issueAt=null;
        }
        return issueAt;
    }

    public String getAudienceFromToken(String token){
        String audience;
        try {
            final Claims claims=this.getAllClaimsFromToken(token);
            audience=claims.getAudience();
        } catch (Exception e) {
            audience=null;
        }
        return audience;
    }

    public String refreshToken(String token,Device device){
        String refreshedToken;
        Date a=timeProvider.now();
        try {
            final Claims claims=this.getAllClaimsFromToken(token);
            claims.setIssuedAt(a);
            refreshedToken= Jwts.builder()
                    .setClaims(claims)
                    .setExpiration(generateExpirationDate(device))
                    .signWith(SINGNATURE_ALGORITHM,SECRET)
                    .compact();
        } catch (Exception e) {
            refreshedToken=null;
        }
        return refreshedToken;
    }


    public String generateToken(String username,Device device){
        String audience=generateAudience(device);
        return Jwts.builder()
                .setIssuer( APP_NAME )
                .setSubject(username)
                .setAudience(audience)
                .setIssuedAt(timeProvider.now())
                .setExpiration(generateExpirationDate(device))
                .signWith(SINGNATURE_ALGORITHM,SECRET)
                .compact();
    }

    private String generateAudience(Device device){
        String audience=AUDIENCE_UNKNON;
        if(device.isNormal()){
            audience=AUDIENCE_WEB;
        }
        else if(device.isTablet()){
            audience=AUDIENCE_TABLET;
        }
        else if(device.isMobile()){
            audience=AUDIENCE_MOBILE;
        }
        return audience;
    }

    private Claims getAllClaimsFromToken(String token){
        Claims claims;
        try {
            claims=Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            claims=null;
        }
        return claims;
    }


    private Date generateExpirationDate(Device device){
        long expiresIn=device.isTablet()||device.isMobile()?MOBILE_EXPIRES_IN:EXPIRES_IN;
        return new Date(timeProvider.now().getTime()+expiresIn*1000);
    }

    public int getExpiredIn(Device device){
        return device.isMobile()||device.isTablet()?MOBILE_EXPIRES_IN:EXPIRES_IN;
    }

    public Boolean validateToken(String token,UserDetails userDetails){
        User user=(User) userDetails;
        final String username=getUsernameFromToken(token);
        final Date created=getIssuredAtDateFromToken(token);
        return (
                username!=null&&
                        username.equals(userDetails.getUsername())&&
                        !isCreatedBeforeLastPasswordReset(created,user.getLastPasswordResetDate())
                );
    }

    private Boolean isCreatedBeforeLastPasswordReset(Date created,Date lastpasswordReset){
        return (
                lastpasswordReset!=null&&created.before(lastpasswordReset)
                );
    }

    public String getToken(HttpServletRequest request){
        String authHeader=getAuthHeaderFromHeader(request);
        if(authHeader!=null&&authHeader.startsWith("Bearer ")){
            return authHeader.substring(7);
        }
        return null;
    }

    public String getAuthHeaderFromHeader(HttpServletRequest request){
        return request.getHeader(AUTH_HEADER);
    }




}
